package com.googlecode.utterlyidle.multipart;

import com.googlecode.totallylazy.Callable1;
import com.googlecode.totallylazy.Callable2;
import com.googlecode.utterlyidle.Entity;

import java.io.UnsupportedEncodingException;
import java.util.List;

import static com.googlecode.totallylazy.Callables.toString;
import static com.googlecode.totallylazy.Sequences.sequence;
import static com.googlecode.utterlyidle.Rfc2616.HTTP_LINE_SEPARATOR;
import static com.googlecode.utterlyidle.multipart.Part.asBytes;

public class Parts {
    private List<Part> parts;
    private Boundary delimiterLine;

    public Parts(List<Part> parts, Boundary boundary) {
        this.parts = parts;
        this.delimiterLine = boundary.prefixed();
    }

    public static Parts parts(List<Part> parts, Boundary boundary) {
        return new Parts(parts, boundary);
    }

    @Override
    public String toString() {
        return sequence(parts).map(toString).map(precedeWith(delimiterLine.toString())).toString(HTTP_LINE_SEPARATOR);
    }

    public byte[] toBytes() throws UnsupportedEncodingException {
        return sequence(parts).map(asBytes()).map(precedeWith(delimiterLine.toBytes())).reduce(toByteArray(HTTP_LINE_SEPARATOR.getBytes(Entity.DEFAULT_CHARACTER_SET)));
    }

    private Callable1<byte[], byte[]> precedeWith(final byte[] delimiterLine) {
        return new Callable1<byte[], byte[]>() {
            @Override
            public byte[] call(byte[] part) throws Exception {
                return ByteArrays.combine(delimiterLine, HTTP_LINE_SEPARATOR.getBytes(Entity.DEFAULT_CHARACTER_SET), part);
            }
        };
    }

    private Callable1<String, String> precedeWith(final String delimiterLine) {
        return new Callable1<String, String>() {
            @Override
            public String call(String part) throws Exception {
                return delimiterLine + HTTP_LINE_SEPARATOR + part;
            }
        };
    }

    private Callable2<byte[], byte[], byte[]> toByteArray(final byte[] separator) {
        return new Callable2<byte[], byte[], byte[]>() {
            @Override
            public byte[] call(byte[] bytes, byte[] part) throws Exception {
                return ByteArrays.combine(bytes, separator, part);
            }
        };
    }
}
